﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Drawing;
using System.Drawing.Imaging;
using System.Drawing.Drawing2D;
using System.Drawing.Text;
using System.Data;
using System.IO;

// Graphics & Charts Class v.1.0
// by George Makarov
// Email: georgemakarov1@yandex.ru

namespace GraphicLib
{
    public class cGraphic
    {
        public delegate DataTable dGetQueryTable(string Query);
        public event dGetQueryTable GetQueryTable;
        public delegate string dBytesToMultipleBytes(Int64 Bytes, bool Short, int Precision);
        public event dBytesToMultipleBytes BytesToMultipleBytes;
        public enum eChartOrientation { Vertical, Horizontal };
        public delegate void dErrorHandler(Exception ex);
        public event dErrorHandler ErrorHandler;

        public string Map;
        public string MapName;
        public string LinkTarget;
        public DataTable DT; //Первое поле - Ось Х
        //Второе поле - Ось Y
        //Третье поле ссылка на подробности
        private Bitmap bitmap;
        public string Query
        {
            set
            {
                if (GetQueryTable != null)
                    DT = GetQueryTable(value);
            }
        }

        public cGraphic()
        {
        }

        public string SaveFile(string FilePath, ImageFormat ImgFormat)
        {
            string FileName = string.Empty;
            if (!Directory.Exists(FilePath))
                Directory.CreateDirectory(FilePath);
            FileName = Path.Combine(FilePath, "temp_" + Guid.NewGuid().ToString() + "." + ImgFormat.ToString());
            bitmap.Save(FileName, ImgFormat);
            return FileName;
        }

        public int CreateChartBar(int Width, int Height, eChartOrientation ChartOrientation)
        {

            if (GetQueryTable == null)
                return 1;
            if (DT.Rows.Count == 0)
                return 2;
            int TopMargin = 0;
            int LeftMargin = 0;
            int RightMargin = 0;
            int BottomMargin = 0;
            switch (ChartOrientation)
            {
                case (eChartOrientation.Horizontal):
                    {
                        TopMargin = 5;
                        LeftMargin = 70;
                        RightMargin = 10;
                        BottomMargin = 40;
                        break;
                    }
                case (eChartOrientation.Vertical):
                    {
                        TopMargin = 10;
                        LeftMargin = 65;
                        RightMargin = 5;
                        BottomMargin = 40;
                        break;
                    }
            }

            try
            {
                SizeF UsedArea = new SizeF(Width - LeftMargin - RightMargin, Height - TopMargin - BottomMargin);
                Map = "<map name=\"" + MapName + "\">";
                bitmap = new Bitmap(Width, Height);
                Graphics g = Graphics.FromImage((Image)bitmap);
                g.SmoothingMode = SmoothingMode.AntiAlias;
                Pen pen = new Pen(Color.Black, 1);
                g.DrawLine(pen, new Point(LeftMargin, Height - BottomMargin), new Point(LeftMargin, TopMargin));
                g.DrawLine(pen, new Point(LeftMargin, Height - BottomMargin), new Point(Width - RightMargin, Height - BottomMargin));
                float BarTotalHeight = UsedArea.Height / DT.Rows.Count;
                float BarTotalWidth = UsedArea.Width / DT.Rows.Count;
                float BarHeight = BarTotalHeight / 2;
                float BarWidth = BarTotalWidth / 2;
                float BarMarginWidth = BarTotalWidth / 4;
                float BarMarginHeight = BarTotalHeight / 4;
                float BarMaxValue = Convert.ToInt64(DT.Compute("MAX([" + DT.Columns[1].ColumnName + "])", ""));
                float BarMaxHeight = UsedArea.Height;
                float BarMaxWidth = UsedArea.Width;

                pen.DashStyle = DashStyle.Dash;


                switch (ChartOrientation)
                {
                    case (eChartOrientation.Vertical):
                        {
                            Font YAxisFont = new Font("Tahoma", 10);
                            SizeF YAxisTextSize;
                            for (float i = 1; i < 11; i++)
                            {
                                int YAxisValue = (int)(BarMaxValue * i / 10);
                                string YAxisText = string.Empty;
                                if (BytesToMultipleBytes != null)
                                    YAxisText = BytesToMultipleBytes(YAxisValue, true, 1);
                                else
                                    YAxisText = YAxisValue.ToString();
                                YAxisTextSize = g.MeasureString(YAxisText, YAxisFont);
                                float Y = Height - BottomMargin - (UsedArea.Height * i / 10);
                                pen.Color = Color.Black;
                                g.DrawString(YAxisText, YAxisFont, pen.Brush, 5, Y - YAxisTextSize.Height / 2);
                                pen.Color = Color.LightGray;
                                g.DrawLine(pen, LeftMargin, Y, Width - RightMargin, Y);
                            }
                            break;
                        }
                    case (eChartOrientation.Horizontal):
                        {
                            Font XAxisFont = new Font("Tahoma", 10);
                            SizeF XAxisTextSize;
                            for (float i = 1; i < 11; i++)
                            {
                                int XAxisValue = (int)(BarMaxValue * i / 10);
                                string XAxisText = string.Empty;
                                if (BytesToMultipleBytes != null)
                                    XAxisText = BytesToMultipleBytes(XAxisValue, true, 1);
                                else
                                    XAxisText = XAxisValue.ToString();
                                XAxisTextSize = g.MeasureString(XAxisText, XAxisFont);
                                float X = LeftMargin + (UsedArea.Width * i / 10);
                                float Y = Height - 15;
                                if ((i % 2) == 0)
                                    Y -= XAxisTextSize.Height;
                                pen.Color = Color.Black;
                                g.DrawString(XAxisText, XAxisFont, pen.Brush, X - XAxisTextSize.Width / 2, Y);
                                pen.Color = Color.LightGray;
                                g.DrawLine(pen, X, TopMargin, X, Height - BottomMargin);
                            }
                            break;
                        }
                }
                pen.DashStyle = DashStyle.Solid;
                pen.Color = Color.Black;
                switch (ChartOrientation)
                {
                    case (eChartOrientation.Vertical):
                        {
                            for (int i = 0; i < DT.Rows.Count; i++)
                            {
                                DataRow DR = DT.Rows[i];
                                BarHeight = BarMaxHeight * Convert.ToInt64(DR[1]) / BarMaxValue;
                                if (Math.Round(BarHeight) != 0)
                                {
                                    RectangleF BarRectF = new RectangleF(i * BarTotalWidth + BarMarginWidth + LeftMargin, Height - BottomMargin - BarHeight, BarWidth, BarHeight);
                                    LinearGradientBrush LGBrush = new LinearGradientBrush(BarRectF, Color.Black, Color.Blue, LinearGradientMode.Horizontal);
                                    ColorBlend colorBlend = new ColorBlend(3);
                                    colorBlend.Colors = new Color[] { Color.Navy, Color.Blue, Color.Navy };
                                    colorBlend.Positions = new float[] { 0, 0.5F, 1 };
                                    LGBrush.InterpolationColors = colorBlend;
                                    //pen = new Pen(LGBrush);
                                    Rectangle BarRect = Rectangle.Round(BarRectF);
                                    g.FillRectangle(LGBrush, BarRect);
                                    Map += "<area shape=\"rect\" coords=\"" + BarRect.Left.ToString() + "," + BarRect.Top.ToString() + "," +
                                        BarRect.Right.ToString() + "," + BarRect.Bottom.ToString() + "\" href=\"" +
                                        DR[2].ToString() + "\" title=\"" + DR[0].ToString() + ": " + BytesToMultipleBytes(Convert.ToInt64(DR[1]), false, 2) + "\" target=\"" + LinkTarget + "\" />";
                                }
                                string XAxisText = DR[0].ToString();
                                Font XAxisFont = new Font("Tahoma", 10);
                                SizeF XAxisTextSizeF = g.MeasureString(XAxisText, XAxisFont);
                                Size XAxisTextSize = Size.Round(XAxisTextSizeF);
                                float XAxisTextPosVert = Height - 15;
                                if ((i % 2) == 0)
                                    XAxisTextPosVert -= XAxisTextSizeF.Height;
                                PointF XAxisTextPosF = new PointF(i * BarTotalWidth + LeftMargin + (BarTotalWidth - XAxisTextSizeF.Width) / 2, XAxisTextPosVert);
                                Point XAxisTextPos = Point.Round(XAxisTextPosF);
                                g.DrawString(XAxisText, XAxisFont, pen.Brush, XAxisTextPosF);
                                Map += "<area shape=\"rect\" coords=\"" + XAxisTextPos.X.ToString() + "," + XAxisTextPos.Y.ToString() + "," +
                                    (XAxisTextPos.X + XAxisTextSize.Width).ToString() + "," + (XAxisTextPos.Y + XAxisTextSize.Height).ToString() + "\" href=\"" +
                                    DR[2].ToString() + "\" title=\"" + DR[0].ToString() + ": " + BytesToMultipleBytes(Convert.ToInt64(DR[1]), false, 2) + "\"  target=\"" + LinkTarget + "\" />";
                            }

                            break;
                        }
                    case (eChartOrientation.Horizontal):
                        {
                            for (int i = 0; i < DT.Rows.Count; i++)
                            {
                                DataRow DR = DT.Rows[i];
                                BarWidth = BarMaxWidth * Convert.ToInt64(DR[1]) / BarMaxValue;
                                if (Math.Round(BarWidth) != 0)
                                {
                                    RectangleF BarRectF = new RectangleF(LeftMargin, i * BarTotalHeight + BarMarginHeight + TopMargin, BarWidth, BarHeight);
                                    LinearGradientBrush LGBrush = new LinearGradientBrush(BarRectF, Color.Black, Color.Blue, LinearGradientMode.Vertical);
                                    ColorBlend colorBlend = new ColorBlend(3);
                                    colorBlend.Colors = new Color[] { Color.Navy, Color.Blue, Color.Navy };
                                    colorBlend.Positions = new float[] { 0, 0.5F, 1 };
                                    LGBrush.InterpolationColors = colorBlend;
                                    Rectangle BarRect = Rectangle.Round(BarRectF);
                                    g.FillRectangle(LGBrush, BarRect);
                                    Map += "<area shape=\"rect\" coords=\"" + BarRect.Left.ToString() + "," + BarRect.Top.ToString() + "," +
                                        BarRect.Right.ToString() + "," + BarRect.Bottom.ToString() + "\" href=\"" +
                                        DR[2].ToString() + "\" title=\"" + DR[0].ToString() + ": " + BytesToMultipleBytes(Convert.ToInt64(DR[1]), false, 2) + "\" target=\"" + LinkTarget + "\" />";
                                }
                                string YAxisText = DR[0].ToString().Replace(' ', '\n');
                                Font YAxisFont = new Font("Tahoma", 10);
                                SizeF YAxisTextSizeF = g.MeasureString(YAxisText, YAxisFont);
                                Size YAxisTextSize = Size.Round(YAxisTextSizeF);
                                PointF YAxisTextPosF = new PointF(5, i * BarTotalHeight + TopMargin + (BarTotalHeight - YAxisTextSizeF.Height) / 2);
                                Point YAxisTextPos = Point.Round(YAxisTextPosF);
                                g.DrawString(YAxisText, YAxisFont, pen.Brush, YAxisTextPosF);
                                Map += "<area shape=\"rect\" coords=\"" + YAxisTextPos.X.ToString() + "," + YAxisTextPos.Y.ToString() + "," +
                                    (YAxisTextPos.X + YAxisTextSize.Width).ToString() + "," + (YAxisTextPos.Y + YAxisTextSize.Height).ToString() + "\" href=\"" +
                                    DR[2].ToString() + "\" title=\"" + DR[0].ToString() + ": " + BytesToMultipleBytes(Convert.ToInt64(DR[1]), false, 2) + "\"  target=\"" + LinkTarget + "\" />";
                            }
                            break;
                        }
                }
            }
            catch (Exception ex)
            {
                if (ErrorHandler != null)
                    ErrorHandler(ex);
            }
            Map += "</map>";
            return 0;
        }

        public int CreateChartCircle(int Width, int Height)
        {
            if (GetQueryTable == null)
                return 1;
            if (DT.Rows.Count == 0)
                return 2;
            int TopMargin = 20;
            int LeftMargin = 100;
            int RightMargin = 100;
            int BottomMargin = 20;
            SizeF UsedArea = new SizeF(Width - LeftMargin - RightMargin, Height - TopMargin - BottomMargin);
            Map = "<map name=\"" + MapName + "\">";
            bitmap = new Bitmap(Width, Height);
            Graphics g = Graphics.FromImage((Image)bitmap);
            g.SmoothingMode = SmoothingMode.AntiAlias;
            float CircleRadius = 0;
            if (UsedArea.Height > UsedArea.Width)
                CircleRadius = UsedArea.Width / 2;
            else
                CircleRadius = UsedArea.Height / 2;
            float TotalBytes = Convert.ToInt64(DT.Compute("SUM([" + DT.Columns[1].ColumnName + "])", ""));

            float StartAngle = 0;
            Random genR = new Random(unchecked((int)(DateTime.Now.Ticks >> 1)));
            Random genG = new Random(unchecked((int)(DateTime.Now.Ticks >> 2)));
            Random genB = new Random(unchecked((int)(DateTime.Now.Ticks >> 4)));
            int LeftTitles = TopMargin;
            int RightTitles = TopMargin;
            for (int i = 0; i < DT.Rows.Count; i++)
            {
                DataRow DR = DT.Rows[i];

                float Bytes = Convert.ToInt64(DR[1]);
                float SectorAngle =  Bytes * 360F / TotalBytes;
                Pen pen = new Pen(Color.Black);
                Point[] PolyPoints = null;
                Color color = Color.FromArgb(genR.Next(80, 200), genG.Next(80, 200), genB.Next(80, 200));
                Rectangle UserTitleRect = new Rectangle();
                DrawSector(g, color, pen, new PointF((UsedArea.Width / 2)+LeftMargin, (UsedArea.Height / 2)+TopMargin), CircleRadius, SectorAngle, StartAngle, ref PolyPoints, DR[0].ToString().Replace(' ','\n'), ref LeftTitles, ref RightTitles, ref UserTitleRect);

                Map += "<area shape=\"poly\" coords=\"";
                foreach (Point PolyPoint in PolyPoints)
                    Map += PolyPoint.X.ToString() + "," + PolyPoint.Y.ToString() + ",";
                Map = Map.Remove(Map.Length - 1);
                Map += "\" href=\"" + DR[2].ToString() + "\" title=\"" + DR[0].ToString() + ": " + BytesToMultipleBytes(Convert.ToInt64(DR[1]), false, 2) + "\" target=\"" + LinkTarget + "\" />";

                Map += "<area shape=\"rect\" coords=\""+UserTitleRect.Left.ToString()+","+UserTitleRect.Top.ToString()+","+UserTitleRect.Right.ToString()+","+UserTitleRect.Bottom.ToString()+
                "\" href=\"" + DR[2].ToString() + "\" title=\"" + DR[0].ToString() + ": " + BytesToMultipleBytes(Convert.ToInt64(DR[1]), false, 2) + "\" target=\"" + LinkTarget + "\" />";

                StartAngle += SectorAngle;
            }
            Map += "</map>";


            return 0;
        }

        public void DrawSector(Graphics g, Color color, Pen pen, PointF CircleCenter, float Radius, float SectorAngle, float StartAngle, ref Point[] SectPoints, string Text, ref int LeftTitles, ref int RightTitles, ref Rectangle UserTitle)
        {
            RectangleF CircleRect = new RectangleF(CircleCenter.X-Radius, CircleCenter.Y-Radius,Radius*2,Radius*2);
            SolidBrush brush = new SolidBrush(color);
            int PointsCount = 11;
            SectPoints = new Point[PointsCount];
            SectPoints[0] = Point.Round(CircleCenter);
            for (int i = 0; i < (PointsCount-1); i++)
            {
                SectPoints[i + 1] = Point.Round(new PointF((float)(Radius * Math.Cos((StartAngle + SectorAngle * i / (PointsCount - 2)) * (Math.PI / 180)) + CircleCenter.X), (float)(Radius * Math.Sin((StartAngle + SectorAngle * i / (PointsCount-2)) * (Math.PI / 180)) + CircleCenter.Y)));
            }
            g.FillPie(brush, Rectangle.Round(CircleRect), StartAngle, SectorAngle);
            //for (int i = 0; i < PointsCount; i++)
            //{
            //    Point Point1;
            //    Point Point2;
            //    Point1 = SectPoints[i];
            //    if (i == (PointsCount-1))
            //        Point2 = SectPoints[0];
            //    else
            //        Point2 = SectPoints[i + 1];
            //    g.DrawLine(pen, Point1, Point2);
            //}
            Point TextLineInSect = Point.Round(new PointF((float)((Radius*0.9) * Math.Cos((StartAngle + SectorAngle / 2) * (Math.PI / 180)) + CircleCenter.X), (float)((Radius*0.9) * Math.Sin((StartAngle + SectorAngle / 2) * (Math.PI / 180)) + CircleCenter.Y)));            
            Font TextFont = new Font("Tahoma", 10);
            SizeF TextSizeF = g.MeasureString(Text, TextFont);
            //Size TextSize = Size.Round(TextSizeF);
            PointF TextPosF;
            PointF Underline1;
            PointF Underline2;
            if (TextLineInSect.X < CircleCenter.X)
            {
                TextPosF = new PointF(CircleCenter.X - Radius - TextSizeF.Width - 5, LeftTitles);
                LeftTitles += Convert.ToInt32(TextSizeF.Height) + 15;
                Underline1 = new PointF(TextPosF.X, TextPosF.Y + TextSizeF.Height + 5);
                Underline2 = new PointF(TextPosF.X + TextSizeF.Width, TextPosF.Y + TextSizeF.Height + 5);
            }
            else
            {
                TextPosF = new PointF(CircleCenter.X + Radius + 5, RightTitles);
                RightTitles += Convert.ToInt32(TextSizeF.Height) + 15;
                Underline1 = new PointF(TextPosF.X + TextSizeF.Width, TextPosF.Y + TextSizeF.Height + 5);
                Underline2 = new PointF(TextPosF.X, TextPosF.Y + TextSizeF.Height + 5); 
            }
            g.DrawLine(pen, Underline1, Underline2);
            g.DrawLine(pen, Underline2, TextLineInSect);
            //Point TextPos = Point.Round(TextPosF);
            g.DrawString(Text, TextFont, pen.Brush, TextPosF);
            UserTitle = Rectangle.Round(new RectangleF(TextPosF, TextSizeF));

        }
    }
}
